package com.wzx.user.service.impl.mongo;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wzx.common.exception.LeadNewsException;
import com.wzx.user.common.RedisPwdErrorNumCommon;
import com.wzx.user.dto.NotesDto;
import com.wzx.user.mapper.*;
import com.wzx.user.pojo.*;
import com.wzx.user.pojo.shop.pojo.ReleaseDynamic;
import com.wzx.user.dto.RedPkgDto;
import com.wzx.user.dto.RedPkgRobDto;
import com.wzx.user.mongo.redis.HbMsg;
import com.wzx.user.service.CommonService;
import com.wzx.user.service.RedisControlService;
import com.wzx.user.service.mongo.RedPacketBasicService;
import com.wzx.user.utils.AmountUtils;
import com.wzx.user.utils.BigDecimalManagerUtils;
import com.wzx.user.utils.RedPacketUtils;
import com.wzx.user.utils.RedisUtils;
import com.wzx.user.vo.RedPkgVo;
import lombok.extern.slf4j.Slf4j;
import net.oschina.j2cache.CacheChannel;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author wzx
 * @version 1.0
 * @date 2022/8/12 - 19:03
 * @description 标题
 */
@Service
@Slf4j
@Transactional
public class RedPacketBasicServiceImpl implements RedPacketBasicService {

    @Autowired
    private CacheChannel cacheChannel;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private WalletMapper walletMapper;

    @Autowired
    private RedPkgMapper redPkgMapper;

    @Autowired
    private NotesMapper notesMapper;

    @Autowired
    private RedPkgRobMapper redPkgRobMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedPacketUtils redPacketUtils;

    @Autowired
    private RedisUtils redisUtil;

    @Autowired
    private CommonService commonService;

    @Autowired
    private RedisPwdErrorNumCommon redisPwdErrorNumCommon;

    @Autowired
    private RedisControlService redisControlService;

    @Autowired
    private UserMapper userMapper;

    @Override
    public RedPkgDto rob(RedPkgVo redPkgVo) throws LeadNewsException {
        //查询用户是否有抢过红包
        boolean b = queryUserIsRob(redPkgVo.getHbId(), redPkgVo.getUserId());
        if (!b) {
            throw new LeadNewsException("用户已经抢过红包！");
        }
        //预减获取红包剩余数量，decr原子减来防止领取人数超过红包个数
        String grabRedPackageKey = redPacketUtils.GRAB_RED_PACKAGE_KEY + redPkgVo.getHbId();

        Long decrement = redisTemplate.opsForValue().decrement(grabRedPackageKey, 1);
        if (decrement == null || decrement < 0) {
            log.info("拆红包失败, redPacketId:{}", redPkgVo.getHbId());
            throw new LeadNewsException("红包被抢光了");
        }

        Integer redPacketAmount = 0;
        RedPkgDto redPkgDto = new RedPkgDto();
        String unpackRedPackageKey = redPacketUtils.UNPACK_RED_PACKAGE_KEY + redPkgVo.getHbId();
        Integer nowIndex = 0;
        try {
            boolean tryLock = redisUtil.tryLock(unpackRedPackageKey);
            if (!tryLock) {
                //分布式锁获取失败, 将抢红包资格加回去, 让其他人可以去抢
                redisUtil.incr(grabRedPackageKey, 1);
                throw new LeadNewsException("拆红包失败！获取锁失败");
            }

            //红包信息缓存
            String redPackageKey = redPacketUtils.RED_PACKAGE_KEY + redPkgVo.getHbId();

            //获取红包相关信息
            redPkgDto = (RedPkgDto) redisUtil.get(redPackageKey);
            if (redPkgDto == null) {
                throw new LeadNewsException("红包被抢光了！");
            }

            //红包分配金额
            int[] generateArr = redPkgDto.getGenerateArr();

            //查询用户信息
            /*QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userId",redPkgVo.getUserId());
            User user = userMapper.selectOne(queryWrapper);*/

            //红包剩下一个
            if (redPkgDto.getSurplusNum() == 1) {
                //剩余红包数
                redPkgDto.setSurplusNum(0);
                redPacketAmount = generateArr[generateArr.length - 1];
                //剩余金额
                redPkgDto.setSurplusAmount(0L);

                nowIndex = generateArr.length - 1;

                //剩下一个无法修改
            }

            //获取已经抢了的人数
            List<Integer> readUserIds = redPkgDto.getReadUserIds();
            int generateIndex = 0;
            if (readUserIds != null) {
                generateIndex = readUserIds.size();
            }

            //double redPacketAmountByDouble = AmountUtils.handlerAmountByDouble(redPacketAmount);
            if (redPkgDto.getSurplusNum() > 1) {
                //分为单位的金额
                //todo 抢到则加1
                if (redPkgDto.getGroupId() != null) {
                    RedPkgDto redPkgPrice = redisControlService.getRedPkgPrice(redPkgDto, redPkgVo.getUserId(), generateArr, generateIndex);
                    redPacketAmount = redPkgPrice.getRobAmountByInt();
                    redPkgDto = redPkgPrice;
                } else {
                    redPacketAmount = generateArr[generateIndex];
                }

                //红包数除以100
                double subtractionDouble = BigDecimalManagerUtils.subtractionDouble(redPkgDto.getSurplusAmount(), AmountUtils.handlerAmountByDouble(redPacketAmount));
                redPkgDto.setSurplusAmount(subtractionDouble);
                redPkgDto.setSurplusNum(redPkgDto.getSurplusNum() - 1);

                nowIndex = generateIndex;


            }
            //成功抢到红包
            if (redPacketAmount != null && redPacketAmount > 0) {
                redPkgDto.getReadUserIds().add(redPkgVo.getUserId());

                List<String> readUserNames = redPkgDto.getReadUserNames();
                readUserNames.add(redPkgVo.getNickname());
                redPkgDto.setReadUserNames(readUserNames);

                // 红包信息
                redisUtil.set(RedPacketUtils.RED_PACKAGE_KEY + redPkgVo.getHbId(), redPkgDto, RedPacketUtils.RED_PACKET_EXPIRE);

                //todo 抢到则加1
                if (redPkgDto.getGroupId() != null) {
                    redisControlService.addControlNum(redPkgDto.getGroupId(), redPkgVo.getUserId());
                }

            }
            //红包被抢完，清除缓存
            if (redPkgDto.getSurplusNum() <= 0) {
                log.info("红包已被抢完, redPacketId:{}, redPacket:{}", redPkgVo.getHbId(), JSON.toJSONString(redPkgDto));

                //清除该红包相关的缓存
                redisUtil.del(grabRedPackageKey, unpackRedPackageKey, redPackageKey);
            }
            log.info("抢红包SUCCESS, redPacketId:{}, redPacketAmount:{}", redPkgVo.getHbId(), redPacketAmount);
        /*} finally {
            //释放锁
            redisUtil.unLock(RedPacketUtils.UNPACK_RED_PACKAGE_KEY + redPkgVo.getHbId());
        }*/

            RedPkg redPkg = new RedPkg();
            BeanUtils.copyProperties(redPkgDto, redPkg);
            redPkg.setSurplusAmount(AmountUtils.handlerAmountByInteger(redPkgDto.getSurplusAmount()));

            //修改数据库记录
            if (redPkg.getSurplusNum() <= 0) {
                //如果是最后一个
                redPkg.setDataState("2");
                redPkg.setLootAllTime(new Date());
                log.info("红包已抢光！时间:{}", new Date());
            }

            redPkgMapper.updateById(redPkg);

            //2，查询用户余额
            QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("uid", redPkgVo.getUserId());
            Wallet wallet = walletMapper.selectOne(queryWrapper);
            if (wallet == null) {
                //3，当数据为空 直接添加数据
                Wallet addWallet = new Wallet();
                addWallet.setBalance(redPacketAmount);
                addWallet.setUid(redPkgVo.getUserId());
                walletMapper.insert(addWallet);
            } else {
                walletMapper.updateUserBalance(redPacketAmount, redPkgVo.getUserId());
            }
        /*else if (wallet.getBalance() == null) {
            wallet.setBalance(redPacketAmount);
            walletMapper.updateById(wallet);
        } else {
            wallet.setBalance(wallet.getBalance() + redPacketAmount);
            walletMapper.updateById(wallet);
        }*/
            log.info("添加余额成功！");

            //4，添加历史数据
            Notes notes = new Notes();
            notes.setType("领取" + redPkg.getSendName() + "发的红包");
            notes.setPrice(redPacketAmount);
            notes.setUserId(redPkgVo.getUserId());
            notes.setState("1");
            notes.setMethod("6");
            notes.setRedPkgId(redPkg.getRedPkgId());
            notes.setRedPkgType(redPkg.getType());
            notesMapper.insert(notes);

            //5，添加进入抢红包信息
            RedPkgRob redPkgRob = new RedPkgRob();
            redPkgRob.setRedPkgId(redPkg.getRedPkgId());
            redPkgRob.setRobUserId(redPkgVo.getUserId());
            redPkgRob.setRobAmount(redPacketAmount);
            //如果是对应指定的手气最佳下标
            if (nowIndex.equals(redPkgDto.getMaxIndex())) {
                redPkgRob.setIsBest("2");
            } else {
                redPkgRob.setIsBest("1");
            }

            redPkgRobMapper.insert(redPkgRob);
            redPkgDto.setRobAmount(AmountUtils.handlerAmountByDouble(redPacketAmount));
            return redPkgDto;

        } finally {
            //释放锁
            redisUtil.unLock(RedPacketUtils.UNPACK_RED_PACKAGE_KEY + redPkgVo.getHbId());
        }
    }

    @Override
    public Integer sendPkg(HbMsg hbMsg) throws LeadNewsException {
        String unpackRedPackageKey = redPacketUtils.SEND_RED_PACKAGE_KEY + hbMsg.getUserId();
        try {
            boolean tryLock = redisUtil.tryLock(unpackRedPackageKey);
            if (!tryLock) {
                throw new LeadNewsException("拆红包失败！获取锁失败");
            }

            //1，查询登录次数
            redisPwdErrorNumCommon.getPwdErrorNum(hbMsg.getUserId(), 1);

            //2，判断支付密码是否正确
            QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("uid", hbMsg.getUserId());
            List<Wallet> wallets = walletMapper.selectList(queryWrapper);
            if (wallets.isEmpty()) {
                throw new LeadNewsException("未设置密码！");
            }

            boolean decrypt = commonService.decrypt(hbMsg.getUserId(), hbMsg.getPwd(), wallets.get(0).getPassword());
            if (!decrypt) {
                log.error("用户：{}，输入密码是：{}", hbMsg.getUserId(), hbMsg.getPwd());
                redisPwdErrorNumCommon.setPwdErrorNum(hbMsg.getUserId(), 1);
                throw new LeadNewsException("密码输入错误！");
            }

            //3，密码输入正确，删除错误次数
            redisPwdErrorNumCommon.closePwdErrorNum(hbMsg.getUserId(), 1);
            //金额转化为分
            Integer allAmount = AmountUtils.handlerAmountByInteger(hbMsg.getAmount());

            //查询用户余额是否足够
            if (wallets.get(0).getBalance() < allAmount) {
                log.error("用户：{}，发{}钱的红包余额不足！", hbMsg.getUserId(), hbMsg.getAmount());
                throw new LeadNewsException("余额不足");
            }

            //2，数据转换存入MySQL
            RedPkg redPkg = new RedPkg();
            redPkg.setSendId(hbMsg.getUserId());
            redPkg.setSendName(hbMsg.getSenderName());
            redPkg.setSendHead(hbMsg.getSenderHead());
            redPkg.setTotalAmount(allAmount);
            redPkg.setTotalNum(hbMsg.getNum());
            redPkg.setTitle(hbMsg.getTitle());
            redPkg.setType(hbMsg.getHbType());
            if (!StringUtils.isEmpty(hbMsg.getGroupId())) {
                redPkg.setGroupId(hbMsg.getGroupId());
            }

            redPkg.setSurplusNum(hbMsg.getNum());
            redPkg.setSurplusAmount(allAmount);
            redPkg.setReadUserIds("");
            redPkg.setDataState("1");

            if (!StringUtils.isEmpty(hbMsg.getAppointUserId()) && !StringUtils.isEmpty(hbMsg.getAppointUserName())) {
                redPkg.setAppointUserId(hbMsg.getAppointUserId());
                redPkg.setAppointUserName(hbMsg.getAppointUserName());
            }

            //3，红包分配
            //int[] ints = redPacketUtils.queryGenerateArr(hbMsg.getAmount(), hbMsg.getNum(), hbMsg.getHbType());
            //int[] ints = com.wzx.user.common.RedPacketUtils.generateArr(allAmount, hbMsg.getNum(), hbMsg.getHbType());
            int[] ints = redPacketUtils.generateArr(allAmount, hbMsg.getNum(), hbMsg.getHbType(), hbMsg.getTitle(), hbMsg.getGroupId(), hbMsg.getUserId());

            if (hbMsg.getHbType().equals("2")) {
                //获取其中手气最佳下标和值
                int maxAmount = ints[0];
                int maxIndex = 0;
                if (ints.length != 1) {
                    for (int i = 1; i < ints.length; i++) {
                        if (ints[i] > maxAmount) {
                            maxAmount = ints[i];
                            maxIndex = i;
                        }
                    }
                }
                redPkg.setMaxAmount(maxAmount);
                redPkg.setMaxIndex(maxIndex);
            }
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < ints.length; i++) {
                stringBuilder.append(ints[i]);
                if (i != (ints.length - 1)) {
                    stringBuilder.append(",");
                }
            }
            redPkg.setGenerateArr(stringBuilder.toString());

            redPkgMapper.insert(redPkg);
            //4，减少用户余额
            //QueryWrapper<Wallet> walletQueryWrapper = new QueryWrapper<>();
            walletMapper.updateUserBalance(-allAmount, hbMsg.getUserId());

        /*walletQueryWrapper.eq("uid", hbMsg.getUserId());
        Wallet wallet = walletMapper.selectOne(walletQueryWrapper);
        wallet.setBalance(wallet.getBalance() - allAmount);
        walletMapper.updateById(wallet);*/

            //5，添加历史数据
            Notes notes = new Notes();
            notes.setType("红包:发送红包");
            notes.setPrice(allAmount);
            notes.setUserId(hbMsg.getUserId());
            notes.setState("1");
            notes.setMethod("5");
            notes.setRedPkgId(redPkg.getRedPkgId());
            notes.setRedPkgType(hbMsg.getHbType());
            notesMapper.insert(notes);
            //6，将从数据库中获取的数据存储到缓存中
            // 抢红包资格缓存（用于记录已经剩余红包数量）
            redisTemplate.opsForValue().set(RedPacketUtils.GRAB_RED_PACKAGE_KEY + redPkg.getRedPkgId(), redPkg.getTotalNum(), RedPacketUtils.RED_PACKET_EXPIRE, TimeUnit.SECONDS);

            RedPkgDto redPkgDto = new RedPkgDto();
            BeanUtils.copyProperties(redPkg, redPkgDto);
            redPkgDto.setReadUserIds(new ArrayList<>());
            redPkgDto.setReadUserNames(new ArrayList<>());
            redPkgDto.setGenerateArr(ints);
            redPkgDto.setTotalAmount(hbMsg.getAmount());
            redPkgDto.setSurplusAmount(hbMsg.getAmount());
            // 红包信息
            redisTemplate.opsForValue().set(RedPacketUtils.RED_PACKAGE_KEY + redPkg.getRedPkgId(), redPkgDto, RedPacketUtils.RED_PACKET_EXPIRE, TimeUnit.SECONDS);

            return redPkg.getRedPkgId();
        } finally {
            //释放锁
            redisUtil.unLock(unpackRedPackageKey);
        }
    }

    @Override
    public void robByUser(Integer hbId, Integer userId) {
        //1，查询红包信息
        RedPkg redPkg = redPkgMapper.selectById(hbId);

        //2，查询用户余额
        QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", userId);
        Wallet wallet = walletMapper.selectOne(queryWrapper);
        if (wallet == null) {
            //3，当数据为空 直接添加数据
            Wallet addWallet = new Wallet();
            addWallet.setBalance(redPkg.getTotalAmount());
            addWallet.setUid(userId);
            walletMapper.insert(addWallet);
        } else {
            walletMapper.updateUserBalance(redPkg.getTotalAmount(), userId);
        }

        //红包信息缓存
        String redPackageKey = redPacketUtils.RED_PACKAGE_KEY + hbId;

        //获取红包相关信息
        RedPkgDto redPkgDto = (RedPkgDto) redisUtil.get(redPackageKey);
        redPkgDto.setSurplusAmount(0d);
        redPkgDto.setSurplusNum(0);
        redisUtil.set(redPackageKey, redPkgDto);

        /*else if (wallet.getBalance() == null) {
            wallet.setBalance(redPkg.getTotalAmount());
            walletMapper.updateById(wallet);
        } else {
            wallet.setBalance(wallet.getBalance() + redPkg.getTotalAmount());
            walletMapper.updateById(wallet);
        }*/
        //4，添加历史数据
        Notes notes = new Notes();
        notes.setType("红包:领取" + redPkg.getSendName() + "发送的红包");
        notes.setPrice(redPkg.getTotalAmount());
        notes.setUserId(userId);
        notes.setState("1");
        notes.setMethod("6");
        notes.setRedPkgId(redPkg.getRedPkgId());
        notes.setRedPkgType(redPkg.getType());
        notesMapper.insert(notes);

        //5，更改发布状态
        redPkg.setSurplusAmount(0);
        redPkg.setSurplusNum(0);
        redPkg.setDataState("2");
        redPkg.setLootAllTime(new Date());
        redPkgMapper.updateById(redPkg);

        //6，添加进入抢红包信息
        RedPkgRob redPkgRob = new RedPkgRob();
        redPkgRob.setRedPkgId(redPkg.getRedPkgId());
        redPkgRob.setRobUserId(userId);
        redPkgRob.setRobAmount(redPkg.getTotalAmount());
        redPkgRobMapper.insert(redPkgRob);
    }

    @Override
    public Map<String, Object> queryRedPkgRobMessage(Integer hbId, Integer userId) {
        Map<String, Object> maps = new HashMap<>();
        RedPkg redPkg = redPkgMapper.selectById(hbId);
        maps.put("redPkg", redPkg);
        //查询用户抢到了多少钱红包
        QueryWrapper<RedPkgRob> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("robUserId", userId);
        queryWrapper.eq("redPkgId", hbId);
        RedPkgRob redPkgRob = redPkgRobMapper.selectOne(queryWrapper);
        if (redPkgRob == null) {
            maps.put("userRobAmount", 0);
        } else {
            maps.put("userRobAmount", redPkgRob.getRobAmount());
        }

        //查询抢到红包记录
        List<RedPkgRobDto> list = redPkgRobMapper.queryRedPkgRobMessage(hbId);
        maps.put("redPkgRob", list);

        return maps;
    }

    @Override
    public RedPkgDto queryRedPkgById(Integer hbId) {
        RedPkg redPkg = redPkgMapper.selectById(hbId);
        RedPkgDto redPkgDto = new RedPkgDto();
        BeanUtils.copyProperties(redPkg, redPkgDto);
        redPkgDto.setSurplusAmount(AmountUtils.handlerAmountByDouble(redPkg.getSurplusAmount()));
        redPkgDto.setTotalAmount(AmountUtils.handlerAmountByDouble(redPkg.getTotalAmount()));
        return redPkgDto;
    }

    @Override
    public boolean queryUserIsRob(Integer hbId, Integer userId) throws LeadNewsException {
        //1，验证某个用户是否抢过红包
        String grabRedPacketId = redPacketUtils.GRAB_RED_PACKAGE_KEY + hbId;
        Integer decrement = (Integer) redisTemplate.opsForValue().get(grabRedPacketId);
        if (decrement == null || decrement < 0) {
            log.info("拆红包失败, redPacketId:{}", hbId);
            throw new LeadNewsException("红包被抢光了");
        }

        //2，获取已经抢过红包的人
        String redPacketKey = redPacketUtils.RED_PACKAGE_KEY + hbId;
        RedPkgDto redPkgDto = (RedPkgDto) redisTemplate.opsForValue().get(redPacketKey);
        List<Integer> readUserIds = redPkgDto.getReadUserIds();
        if (!StringUtils.isEmpty(readUserIds)) {
            for (Integer readUserId : readUserIds) {
                if (readUserId.equals(userId)) {
                    //该用户已经抢过红包了
                    log.info("用户：{}已经抢过红包： redPacketId:{}", userId, hbId);
                    throw new LeadNewsException("您已经抢过红包！");
                }
            }
        }
        return true;
    }

    @Override
    public Map<String, Object> queryRedPkgByRecord(RedPkgVo redPkgVo) {
        Double sumPrice = notesMapper.querySunPrice(redPkgVo);
        Page<Notes> page = new Page<>(redPkgVo.getPage(), redPkgVo.getSize());
        Page<NotesDto> resultPage = notesMapper.queryByTypePage(redPkgVo.getUserId(),
                redPkgVo.getQueryType(),
                redPkgVo.getYear(),
                page);
        //数据处理
        List<NotesDto> resultList = resultPage.getRecords();
        for (NotesDto notesObject : resultList) {
            if (notesObject.getMethod().equals("5") && notesObject.getRedPkgId() != null) {
                //查询redis
                //红包信息缓存
                String redPackageKey = RedPacketUtils.RED_PACKAGE_KEY + notesObject.getRedPkgId();
                //获取红包相关信息
                RedPkgDto redPkgDto = (RedPkgDto) redisUtil.get(redPackageKey);
                if (redPkgDto != null && redPkgDto.getSurplusNum() != null) {
                    notesObject.setSurplusNum(redPkgDto.getSurplusNum());
                    notesObject.setTotalNum(redPkgDto.getTotalNum());
                }
            }
        }
        resultPage.setRecords(resultList);

        Map<String, Object> map = new HashMap<>();
        map.put("sumPrice", sumPrice);
        map.put("resultPage", resultPage);

        return map;
    }

    @Override
    public List<RedPkgDto> robRedPkgByAllUsers(Integer hbId, String userIds) {
        List<RedPkgDto> resultList = new ArrayList<>();
        /*String[] userIdArr = userIds.split(",");
        for (String userId : userIdArr) {
            try {
                RedPkgDto rob = rob(hbId, Integer.parseInt(userId));
                rob.setRobUserId(Integer.parseInt(userId));
                resultList.add(rob);
            } catch (LeadNewsException e) {
                log.error("用户{}抢红包失败：", userId, e);
            }
        }*/
        return resultList;
    }

    @Override
    public HbMsg sendPkgByUserBalance(HbMsg hbMsg) throws LeadNewsException {
        QueryWrapper<Wallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", hbMsg.getUserId());
        Wallet wallet = walletMapper.selectOne(queryWrapper);
        if (wallet == null || wallet.getBalance() == null || wallet.getBalance() <= 0) {
            hbMsg.setAmount(0d);
            return hbMsg;
        }
        double amount = AmountUtils.handlerAmountByDouble(wallet.getBalance());
        hbMsg.setAmount(amount);
        Integer hbId = null;
        try {
            hbId = sendPkg(hbMsg);
        } catch (LeadNewsException e) {
            log.error(e.getMessage());
            throw e;
        }
        hbMsg.setRedPkgId(hbId);
        return hbMsg;

    }


}